package com.ccrfid.rmc6.appconfig.websocket;

import com.ccrfid.rmc6.lbservice3.client.WsClient;
import com.ccrfid.rmc6.lbservice3.manager.ClientManager;
import com.ccrfid.rmc6.pojo.Constants;
import com.ccrfid.rmc6.resource.auth.pojo.JwtToken;
import com.ccrfid.rmc6.service.auth.AuthenticationService;
import com.ccrfid.rmc6.util.JsonUtil;
import com.ccrfid.rmc6.util.NetUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
    private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class);
    private Map<WebSocketSession, Long> waitSessions;
    @Autowired
    private ClientManager clientManager;
    @Autowired
    private AuthenticationService authenticationService;

    @PostConstruct
    public void init() {
        waitSessions = new ConcurrentHashMap<>();

        //定时检查，剔除没有正确协议连接的websocket client
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        TimeUnit.SECONDS.sleep(60);
                        if (!waitSessions.isEmpty()) {
                            Long timeNow = new Date().getTime();
                            Iterator<Map.Entry<WebSocketSession, Long>> iterator = waitSessions.entrySet().iterator();
                            while (iterator.hasNext()) {
                                Map.Entry<WebSocketSession, Long> entry = iterator.next();
                                Long createTime = entry.getValue();
                                //剔除超过60秒没有进行正确认证的客户端
                                if (timeNow > createTime && timeNow - createTime > 60 * 1000) {
                                    WebSocketSession socketSession = entry.getKey();
                                    logger.debug("Websocket -- remove timeout client, clientId:{}, clientIp:{}",
                                            socketSession.getId(), socketSession.getRemoteAddress().getHostString());
                                    if (socketSession.isOpen()) {
                                        socketSession.close(CloseStatus.TLS_HANDSHAKE_FAILURE);
                                    }
                                    iterator.remove();
                                }
                            }
                        }
                    } catch (InterruptedException ex) {
                        logger.error(ex.getMessage());
                        ex.printStackTrace();
                    } catch (IOException ex) {
                        logger.error(ex.getMessage());
                        ex.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        waitSessions.put(session, new Date().getTime());
        logger.debug("Websocket -- afterConnectionEstablished, clientId: {}, clientIp: {}",
                session.getId(), session.getRemoteAddress().getHostString());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String clientId = session.getId();
        String clientIp = session.getRemoteAddress().getHostString();
        logger.debug("Websocket -- handleTextMessage, clientId: {}, clientIp: {}, message: {}", clientId, clientIp, message.getPayload());

        JwtToken jwtToken = JsonUtil.INSTANCE.readValue(message.getPayload(), JwtToken.class);
        //验证token
        if (jwtToken != null) {
            if (jwtToken.getAuthType().equalsIgnoreCase("keep-alive")) {
                //keep-alive
                MsgEntity<String> msg = new MsgEntity<>("keep-alive", session.getId());
                session.sendMessage(new TextMessage(JsonUtil.INSTANCE.writeValueAsString(msg)));
            } else if (jwtToken.getAuthType().equalsIgnoreCase("Bearer")
                    && authenticationService.validateToken(jwtToken.getToken())) {
                Claims claims = Jwts.parser().setSigningKey(Constants.signKey).parseClaimsJws(jwtToken.getToken()).getBody();
                Integer userId = (Integer) claims.get("userId");
                String userAccount = (String) claims.get("account");

                //验证通过，回复client id
                MsgEntity<String> msg = new MsgEntity<>("clientId", session.getId());
                session.sendMessage(new TextMessage(JsonUtil.INSTANCE.writeValueAsString(msg)));
                logger.debug("Websocket -- Authentication success, clientId: {}", session.getId());

                List<String> clientIpList = new ArrayList<>();
                clientIpList.add(clientIp);
                if (clientIp.equals("127.0.0.1") || clientIp.equals("0:0:0:0:0:0:0:1")) {
                    Set<String> localIpSet = NetUtil.getLocalIpAddresses();
                    if (localIpSet.size() > 0) {
                        for (String ip : localIpSet) {
                            clientIpList.add(ip);
                        }
                    }
                }
                clientManager.addWebClient(new WsClient(clientId, userAccount, clientIpList, session));
                waitSessions.remove(session);
                logger.debug("Websocket -- addWebClient, clientId: {}, userAccount:{}, clientIp: {}", clientId, userAccount, clientIp);
            } else {
                MsgEntity<String> msg = new MsgEntity<>("error", "Authentication failed");
                session.sendMessage(new TextMessage(JsonUtil.INSTANCE.writeValueAsString(msg)));
                logger.debug("Websocket -- Authentication failed, clientId: {}", clientId);
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        logger.debug("Websocket -- afterConnectionClosed, clientId: {}, clientIp: {}",
                session.getId(), session.getRemoteAddress());

        clientManager.deleteWebClient(session.getId());
        logger.debug("Websocket -- deleteWebClent, clientId: {}, clientIp: {}", session.getId(), session.getRemoteAddress());
    }
}
